﻿Imports FairShop.Objects
Imports FairShop.Operaties

Namespace Presenters

    ''' <summary>
    ''' Deze presenter bevat UI-logica voor VerkoopDetailsForm
    ''' Als de gebruiker iets doet, stuurt de view (het scherm) sommige zaken door naar hier.
    ''' Hier worden ze verwerkt, en van hieruit wordt de view (het scherm) bijgewerkt.
    ''' Op deze manier kan de logica in deze klasse met unittests volledig getest worden.
    ''' Zie ook het design pattern Model View Presenter
    ''' </summary>
    Public Class VerkoopDetailsPresenter

#Region "Private declarations"

        ' details van de huidige verkoop alleen
        Private _verkoop As ArtikelsDataSet.VerkopenRow

        Private _aantalAfgerekendeKlanten As Integer
        Private _teBetalen As Decimal
        Private _teBetalenGepauzeerd As Decimal = 0
        Private _totaalVerkocht As Decimal
        Private _huidigeKlantVolgnummer As Integer
        Private _view As VerkoopDetailsForm
        Private _kanInOfUitWachtZetten As Boolean
        Private _kanKlantOngedaanMaken As Boolean
        Private _kanAfrekenen As Boolean
        Private _kanLeveringsbonMaken As Boolean

#End Region

#Region "Public property's"

        Public Property AantalAfgerekendeKlanten() As Integer
            Get
                Return _aantalAfgerekendeKlanten
            End Get
            Set(ByVal value As Integer)
                _aantalAfgerekendeKlanten = value
                If (_view IsNot Nothing) Then _view.lblAantalKlanten.Text = value.ToString()
            End Set
        End Property

        Public Property TeBetalen() As Decimal
            Get
                Return _teBetalen
            End Get
            Set(ByVal value As Decimal)
                _teBetalen = value
                If (_view IsNot Nothing) Then _view.lblTeBetalen.Text = value.ToString("c")
            End Set
        End Property

        Public Property TotaalVerkocht() As Decimal
            Get
                Return _totaalVerkocht
            End Get
            Set(ByVal value As Decimal)
                _totaalVerkocht = value
                If (_view IsNot Nothing) Then _view.lblTotaalVerkocht.Text = value.ToString("c")
            End Set
        End Property

        Public ReadOnly Property TotaalCashVerkocht() As Decimal
            Get
                Return _totaalVerkocht - _verkoop.BedragBancontact - _verkoop.BedragMaaltijdcheques
            End Get
        End Property

        Public ReadOnly Property Verkoop() As ArtikelsDataSet.VerkopenRow
            Get
                Return _verkoop
            End Get
        End Property

        Public ReadOnly Property HuidigeKlantVolgnummer() As Integer
            Get
                Return _huidigeKlantVolgnummer
            End Get
        End Property

        Public Property KanKlantOngedaanMaken() As Boolean
            Get
                Return _kanKlantOngedaanMaken
            End Get
            Set(ByVal value As Boolean)
                _kanKlantOngedaanMaken = value
                If (_view IsNot Nothing) Then _view.tsbOngedaan.Enabled = value
            End Set
        End Property

        Public Property KanInOfUitWachtZetten() As Boolean
            Get
                Return _kanInOfUitWachtZetten
            End Get
            Set(ByVal value As Boolean)
                _kanInOfUitWachtZetten = value
                If (_view IsNot Nothing) Then _view.tsbPauze.Enabled = value
            End Set
        End Property

        Public Property KanAfrekenen() As Boolean
            Get
                Return _kanAfrekenen
            End Get
            Set(ByVal value As Boolean)
                _kanAfrekenen = value
                If (_view IsNot Nothing) Then
                    _view.tsbAfrekenen.Enabled = value
                End If
            End Set
        End Property

        Public Property KanLeveringsbonMaken() As Boolean
            Get
                Return _kanLeveringsbonMaken
            End Get
            Set(ByVal value As Boolean)
                _kanLeveringsbonMaken = value
                If (_view IsNot Nothing) Then
                    _view.tsbLeveringsbon.Enabled = value
                End If
            End Set
        End Property

#End Region

#Region "Constructor/initialisatie"

        Public Sub New(ByVal view As VerkoopDetailsForm, ByVal verkoop As ArtikelsDataSet.VerkopenRow)
            _view = view
            _verkoop = verkoop
            AantalAfgerekendeKlanten = 0
            _huidigeKlantVolgnummer = 1
            _totaalVerkocht = 0D
            _teBetalen = 0D
            KanKlantOngedaanMaken = False ' veronderstelt lege datatable
            KanInOfUitWachtZetten = False
            KanAfrekenen = False
            KanLeveringsbonMaken = False
        End Sub

#End Region

#Region "Publieke methodes"

        Public Sub ToevoegenVerkoopsDetail(ByVal verkoopRij As ArtikelsDataSet.VerkopenRow, ByVal artikelRij As ArtikelsDataSet.ArtikelsRow, _
                ByVal prijs As Decimal, ByVal extraOmschrijving As String, ByVal aantal As Integer)
            If (verkoopRij.Id <> Verkoop.Id) Then
                Throw New Exception("Interne fout: nummer van de verkoop klopt niet")
            End If
            Dim detail As New VerkoopsDetail(verkoopRij, artikelRij, prijs, extraOmschrijving, aantal, _huidigeKlantVolgnummer)
            Dim ds As ArtikelsDataSet = DirectCast(verkoopRij.Table.DataSet, ArtikelsDataSet)
            ds.VerkoopsDetails.AddVerkoopsDetailsRow(detail.DataRow)
            TeBetalen += detail.TotaalPrijs()
            UpdateStatusKnoppen()
        End Sub

        Public Sub VerwijderenVerkoopsDetail(ByVal verkoopsdetailRij As ArtikelsDataSet.VerkoopsDetailsRow)
            Dim verkoopsDetail As New VerkoopsDetail(verkoopsDetailRij)
            ' alleen niet afgerekende items kunnen verwijderd worden
            If (Not verkoopsDetail.IsAfgerekend) Then
                Dim verwijderdeRijStondInWacht As Boolean = False
                Dim volgnummerVanKlantInWacht As Integer = 0
                Select Case verkoopsdetailRij.Status
                    Case StatusImage.Huidig
                        TeBetalen -= verkoopsDetail.TotaalPrijs()
                    Case StatusImage.InWacht
                        verwijderdeRijStondInWacht = True
                        volgnummerVanKlantInWacht = verkoopsdetailRij.VolgnummerKlant
                        _teBetalenGepauzeerd -= verkoopsDetail.TotaalPrijs()
                    Case StatusImage.Afgerekend
                        Throw New Exception("Alleen niet afgerekende items kunnen verwijderd worden.")
                End Select
                Dim dt As ArtikelsDataSet.VerkoopsDetailsDataTable = DirectCast(verkoopsdetailRij.Table, ArtikelsDataSet.VerkoopsDetailsDataTable)
                dt.RemoveVerkoopsDetailsRow(verkoopsdetailRij)
                If (verwijderdeRijStondInWacht) Then
                    Dim klantInWachtHeeftNogItems As Boolean = (From vd In Verkoop.GetVerkoopsDetailsRows() _
                                                                Where vd.Status = StatusImage.InWacht).Any()
                    If (Not klantInWachtHeeftNogItems) Then
                        ' de klant die in wacht stond heeft geen items meer. Deze mag dus weg.
                        _teBetalenGepauzeerd = 0D ' zou in feite al zo moeten zijn
                        '  alle volgnummers die na de verwijderde klant kwamen moeten met 1 verminderd worden
                        For Each detailRij As ArtikelsDataSet.VerkoopsDetailsRow In Verkoop.GetVerkoopsDetailsRows
                            If (detailRij.VolgnummerKlant > volgnummerVanKlantInWacht) Then
                                detailRij.VolgnummerKlant -= 1
                            End If
                        Next
                        If (_huidigeKlantVolgnummer > volgnummerVanKlantInWacht) Then
                            _huidigeKlantVolgnummer -= 1
                        End If
                    End If
                End If
            End If
            ' omdat dit binnen een lus aangeroepen wordt, gebeurt UpdateStatusKnoppen in het aanroepende scherm, en slechts éénmaal na de lus
        End Sub

        Public Sub KlantInOfUitWachtZetten()
            If (Not KanInOfUitWachtZetten) Then Return

            Dim erIsEenKlantInWachtGegaan As Boolean = False
            Dim erIsEenKlantUitWachtGekomen As Boolean = False
            Dim nummerVanKlantDieUitWachtKomt As Integer = 0
            For Each row As ArtikelsDataSet.VerkoopsDetailsRow In Verkoop.GetVerkoopsDetailsRows
                ' rijen van de laatste klant gaan naar de status "gepauzeerd"
                ' als er al gepauzeerde rijen waren, worden die opnieuw "huidig"
                If (row.Status = StatusImage.Huidig) Then
                    row.Status = StatusImage.InWacht
                    erIsEenKlantInWachtGegaan = True
                ElseIf (row.Status = StatusImage.InWacht) Then
                    row.Status = StatusImage.Huidig
                    erIsEenKlantUitWachtGekomen = True
                    nummerVanKlantDieUitWachtKomt = row.VolgnummerKlant
                End If
            Next
            If (erIsEenKlantInWachtGegaan And Not erIsEenKlantUitWachtGekomen) Then
                _huidigeKlantVolgnummer = _aantalAfgerekendeKlanten + 2 ' plus 2 omdat er nog een klant in wacht staat
            End If
            If (erIsEenKlantUitWachtGekomen) Then
                _huidigeKlantVolgnummer = nummerVanKlantDieUitWachtKomt
            End If

            Dim temp As Decimal = TeBetalen
            TeBetalen = _teBetalenGepauzeerd
            _teBetalenGepauzeerd = temp

            UpdateStatusKnoppen()
        End Sub

        ''' <summary>
        ''' Maak een klant ongedaan.
        ''' Bij leveringsbonnen is het mogelijk dat de verkoopsdetails al uit de verkoop weg zijn (verplaatst naar de leveringsbon),
        ''' in dat geval moet je het volgnummer van de verwijderde klant meegeven.
        ''' </summary>
        ''' <param name="volgnummerVanVerwijderdeKlant">Nodig als de verkoopsdetails al weg zijn.</param>
        Public Sub KlantOngedaanMaken(Optional ByVal volgnummerVanVerwijderdeKlant As Integer = 0)
            If (Not KanKlantOngedaanMaken) Then Return

            Dim erIsEenKlantUitWachtGekomen As Boolean = False
            ' achterstevoren omdat er rijen verwijderd kunnen worden tijdens de lus
            Dim details() As ArtikelsDataSet.VerkoopsDetailsRow = Verkoop.GetVerkoopsDetailsRows()
            For i As Integer = details.Count - 1 To 0 Step -1
                Dim detail As ArtikelsDataSet.VerkoopsDetailsRow = details(i)
                Select Case detail.Status
                    Case StatusImage.Huidig
                        ' rijen van de huidige klant gaan weg
                        volgnummerVanVerwijderdeKlant = detail.VolgnummerKlant
                        If (detail.RowState = DataRowState.Added) Then
                            detail.Delete()
                        Else
                            Throw New Exception("Reeds bewaarde rij kan niet ongedaan gemaakt worden")
                        End If
                    Case StatusImage.InWacht
                        ' gepauzeerde klant wordt opnieuw de huidige klant
                        detail.Status = StatusImage.Huidig
                        _huidigeKlantVolgnummer = detail.VolgnummerKlant
                        erIsEenKlantUitWachtGekomen = True
                End Select
            Next
            '            If (Not erIsEenKlantUitWachtGekomen AndAlso volgnummerVanVerwijderdeKlant <> _huidigeKlantVolgnummer) Then
            '#If DEBUG Then
            '                ' Bij het verplaatsen van verkoopsdetails naar een leveringsbon kan dit gebeuren
            '                ' omdat er dan niets verwijderd wordt (het is al weg door de idVerkoop te verzetten)
            '                ' maar er wordt wel OngedaanMaken aangeroepen om een eventuele klant in wacht terug
            '                ' te halen en vooral om de nummering en de totalen weer juist te zetten
            '                Throw New Exception("Fout tijdens ongedaan maken") ' zou niet mogen kunnen
            '#Else
            '                Return ' om de gebruiker hier niet mee op te zadelen
            '#End If
            '            End If

            If (volgnummerVanVerwijderdeKlant > 0) Then
                ' er is een klant verwijderd
                '  alle volgnummers die na de verwijderde klant kwamen moeten met 1 verminderd worden
                For Each detailRij As ArtikelsDataSet.VerkoopsDetailsRow In Verkoop.GetVerkoopsDetailsRows
                    If (detailRij.VolgnummerKlant > volgnummerVanVerwijderdeKlant) Then
                        detailRij.VolgnummerKlant -= 1
                        If (detailRij.Status = StatusImage.Afgerekend AndAlso detailRij.Id > 0) Then
                            ' bewaar (alleen bij al eerder bewaarde details) de wijziging van volgnummer
                            ' detailRij.Id > 0 omdat bij unittests van de presenter (waarbij de details
                            ' niet bewaard worden en dus nog in de Added-state staan) dit concurrency-fouten geeft
                            Dal.VerkoopsDetailsBewaren(New ArtikelsDataSet.VerkoopsDetailsRow() {detailRij})
                        End If
                    End If
                Next
            End If
            If (Not erIsEenKlantUitWachtGekomen) Then
                ' we beginnen aan een nieuwe klant nu
                _huidigeKlantVolgnummer = AantalAfgerekendeKlanten + 1
            End If
            If (volgnummerVanVerwijderdeKlant > 0) Then
                ' er is een klant verwijderd
                If (erIsEenKlantUitWachtGekomen AndAlso _huidigeKlantVolgnummer > volgnummerVanVerwijderdeKlant) Then
                    ' als de klant die uit wacht gekomen is na de verwijderde klant kwam, is zijn volgnummer verlaagd
                    _huidigeKlantVolgnummer -= 1
                End If
            End If

            TeBetalen = _teBetalenGepauzeerd
            _teBetalenGepauzeerd = 0D
            UpdateStatusKnoppen()
        End Sub

        Public Sub KlantAfrekenen()
            If (Not KanAfrekenen) Then Return

            AantalAfgerekendeKlanten += 1
            _huidigeKlantVolgnummer = AantalAfgerekendeKlanten + 1 ' behalve als er nog een klant uit wacht komt, zie hieronder
            For Each row As ArtikelsDataSet.VerkoopsDetailsRow In Verkoop.GetVerkoopsDetailsRows
                If (row.Status = StatusImage.Huidig) Then
                    row.Status = StatusImage.Afgerekend
                    row.AcceptChanges() ' anders is de rij opnieuw "Modified". Veronderstelt dat de rest al wel bewaard is!
                ElseIf (row.Status = StatusImage.InWacht) Then
                    ' gepauzeerde klant wordt opnieuw de huidige klant
                    row.Status = StatusImage.Huidig
                    _huidigeKlantVolgnummer = row.VolgnummerKlant
                End If
            Next
            TotaalVerkocht += TeBetalen
            TeBetalen = _teBetalenGepauzeerd
            _teBetalenGepauzeerd = 0D
            UpdateStatusKnoppen()
        End Sub

        Public Sub UpdateStatusKnoppen()
            KanAfrekenen = False
            KanLeveringsbonMaken = False
            KanKlantOngedaanMaken = False
            KanInOfUitWachtZetten = False
            For Each detailRij As ArtikelsDataSet.VerkoopsDetailsRow In Verkoop.GetVerkoopsDetailsRows
                If (detailRij.Status = StatusImage.Huidig) Then
                    KanAfrekenen = True
                    KanLeveringsbonMaken = True
                    KanKlantOngedaanMaken = True
                    KanInOfUitWachtZetten = True
                ElseIf (detailRij.Status = StatusImage.InWacht) Then
                    KanInOfUitWachtZetten = True
                End If
            Next
        End Sub

        Public Sub ZetDetailsLeveringsbonOpKasblad(ByVal idLeveringsbon As Integer, _
                ByVal kasbladVerkoop As ArtikelsDataSet.VerkopenRow)
            If (kasbladVerkoop.Id <> _verkoop.Id) Then
                Throw New Exception("Interne fout: nummer van de verkoop klopt niet")
            End If
            Dim ds As ArtikelsDataSet = Dal.VerkoopsDetailsVanLeveringsbonAnnuleren(idLeveringsbon)
            For Each detail As ArtikelsDataSet.VerkoopsDetailsRow In ds.VerkoopsDetails
                Dim artikelRij As ArtikelsDataSet.ArtikelsRow = Utility.Data.Artikels.FindById(detail.IdArtikel)
                Dim prijs As Decimal = detail.Prijs
                Dim aantal As Integer = detail.Aantal
                Dim extraOmschrijving As String = detail.ExtraOmschrijving
                ToevoegenVerkoopsDetail(kasbladVerkoop, artikelRij, prijs, extraOmschrijving, aantal)
            Next
        End Sub

        Public Sub VerkoopsDetailsBewarenBijBestaandeLeveringsbon(ByVal idVerkoopLeveringsbon As Integer, ByVal consignatie As Boolean)
            Dal.VerkoopsDetailsBewarenBijBestaandeLeveringsbon(Verkoop, idVerkoopLeveringsbon, consignatie)
            KlantOngedaanMaken()
        End Sub

        Public Function VerkoopsDetailsBewarenBijNieuweLeveringsbon( _
                 ByVal idKlant As Integer, ByVal omschrijving As String, ByVal idWinkelier As Integer, ByVal consignatie As Boolean) As Integer
            Dim verkoopsdetails As IEnumerable(Of ArtikelsDataSet.VerkoopsDetailsRow) = From vd In Verkoop.GetVerkoopsDetailsRows() _
                                                                                        Where vd.Status = StatusImage.Huidig
            If (Not verkoopsdetails.Any) Then
                Throw New Exception("Geen verkoopsdetails gevonden om op de leveringsbon te zetten.")
            End If
            ' volgnummer klant bijhouden, omdat we die tijdens KlantOngedaanMaken nog moeilijk kunnen terugvinden
            Dim volgnummerKlant As Integer = verkoopsdetails.First.VolgnummerKlant
            Dim idVerkoopLeveringsbon As Integer = _
                ArtikelsBL.VerkoopsDetailsBewarenBijNieuweLeveringsbon(Verkoop, idKlant, omschrijving, idWinkelier, consignatie)
            KlantOngedaanMaken(volgnummerKlant)
            Return idVerkoopLeveringsbon
        End Function

#End Region

    End Class

End Namespace
